package com.gmrz.uaf.protocol.v1.processor;

import com.gmrz.uaf.common.Constants;
import com.gmrz.uaf.common.GuiceUtil;
import com.gmrz.uaf.db.DAOException;
import com.gmrz.uaf.db.UAFDAOFactory;
import com.gmrz.uaf.db.UAFDBConnectionMgr;
import com.gmrz.uaf.db.dao.AuthenticatorDAO;
import com.gmrz.uaf.db.dao.AuthenticatorSpecDAO;
import com.gmrz.uaf.protocol.v1.processor.exception.AuthenticatorNotRegisteredException;
import com.gmrz.uaf.protocol.v1.processor.exception.UAFErrorCode;
import com.gmrz.uaf.protocol.v1.schema.*;
import com.gmrz.uaf.remote.plugin.AuthServicePluginManager;
import com.gmrz.uaf.remote.plugin.UASServiceProvider;
import com.gmrz.uas.plugin.authservice.AuthServicePlugin;
import com.gmrz.uas.plugin.caservice.CertServicePlugin;
import com.gmrz.uas.plugin.caservice.bean.CertBean;
import com.gmrz.uas.plugin.exception.PluginException;
import com.gmrz.uas.plugin.provider.ServiceProvider;
import com.gmrz.util.CryUtil;
import com.gmrz.util.Strings;
import com.gmrz.util.db.DBUtil;
import com.google.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class UAFDeregisterProcessor extends UAFBaseProcessor {
    private static final Logger LOG = LogManager.getLogger(UAFDeregisterProcessor.class);

    AuthServicePluginManager serviceManager;

    @Inject
    public void setServiceManager(AuthServicePluginManager serviceManager) {
        this.serviceManager = serviceManager;
    }

    public DeregisterationRequest deleteOne(String userName, String appID, String deviceID, String authType
            , String transType, String from, String cancelReason)
            throws DAOException, AuthenticatorNotRegisteredException {
        return deleteOne( userName,  appID,  deviceID,  authType
                ,  transType,  from,  cancelReason,null);

    }
    public DeregisterationRequest deleteOne(String userName, String appID, String deviceID, String authType
            , String transType, String from, String cancelReason,String keyID)
            throws DAOException, AuthenticatorNotRegisteredException {

        Set<Authenticator> authenticators = getAuthenticatorsOnOneDeviceDEL(userName, appID, deviceID, authType, transType,
                Constants.AuthenticatorStatus.ACTIVE.getStatus());
        // 判断认证方式是证书等 或 是本地指纹、人脸等
        if ((authType.compareTo(Constants.AuthType.CERT_FINGERPRINT.getAuthType()) >= 0) || (authType.compareTo(Constants.AuthType.REMOTE_FACEPRINT.getAuthType()) < 0)) {
            String applicationID = this.getServerConfig().getApplicationID(appID, transType);
            if (this.getServerConfig().getTenantShare() && com.gmrz.util.Strings.isNotBlank(applicationID)) {
                List<String> shareAppIDList = getShareAppID(applicationID, transType);
                if (null != shareAppIDList && !shareAppIDList.isEmpty()) {
                    authenticators.clear();
                    for (String shareAppID : shareAppIDList) {
                        Set<Authenticator> shareAuthenticators = getAuthenticatorsOnOneDeviceDEL(userName, shareAppID, deviceID, authType, transType,
                                Constants.AuthenticatorStatus.ACTIVE.getStatus());
                        if (null != shareAuthenticators && !shareAuthenticators.isEmpty()) {
                            authenticators.addAll(shareAuthenticators);
                        }
                    }
                }
            }
        }
        if ((authenticators == null || authenticators.isEmpty())) {
            LOG.error("Authenticator with appID[{}],deviceID[{}],authType[{}],user[{}] is not registered.",
                    appID, deviceID, authType, userName);

            throw new AuthenticatorNotRegisteredException(UAFErrorCode.BIZ_AUTHENTICATOR_NOT_FOUND);
        }
        Set<Authenticator> set = new HashSet<Authenticator>();
        if(Strings.isNotBlank(keyID)){
            for(Authenticator a : authenticators){
                if(keyID.equals(a.getKeyID().toString())){
                    set.add(a);
                }
            }
        }else {
            set.addAll(authenticators);
        }
        DeregisterationRequest request = processDereg(set, deviceID, from, appID, cancelReason);
        request.getOperationHeader().setApplicationID(this.getServerConfig().getApplicationID(appID, transType));
        if (serverConfig.getAppIdUrlBoolean(appID, transType)) {
            setFacetsAaid(request, appID);
        }
        LOG.info("Authenticator with appID[{}],deviceID[{}],authType[{}],user[{}] deregistered successfully.",
                appID, deviceID, authType, userName);
        return request;
    }
    public DeregisterationRequest deleteOthers(String userName, String appID, String deviceID, String authType
            , String transType, String from, String cancelReason,String keyID)
            throws DAOException, AuthenticatorNotRegisteredException {

        Set<Authenticator> authenticators = getAuthenticatorsOnOneDeviceDEL(userName, appID, deviceID, authType, transType,
                Constants.AuthenticatorStatus.ACTIVE.getStatus());
        // 判断认证方式是证书等 或 是本地指纹、人脸等
        if ((authType.compareTo(Constants.AuthType.CERT_FINGERPRINT.getAuthType()) >= 0) || (authType.compareTo(Constants.AuthType.REMOTE_FACEPRINT.getAuthType()) < 0)) {
            String applicationID = this.getServerConfig().getApplicationID(appID, transType);
            if (this.getServerConfig().getTenantShare() && com.gmrz.util.Strings.isNotBlank(applicationID)) {
                List<String> shareAppIDList = getShareAppID(applicationID, transType);
                if (null != shareAppIDList && !shareAppIDList.isEmpty()) {
                    authenticators.clear();
                    for (String shareAppID : shareAppIDList) {
                        Set<Authenticator> shareAuthenticators = getAuthenticatorsOnOneDeviceDEL(userName, shareAppID, deviceID, authType, transType,
                                Constants.AuthenticatorStatus.ACTIVE.getStatus());
                        if (null != shareAuthenticators && !shareAuthenticators.isEmpty()) {
                            authenticators.addAll(shareAuthenticators);
                        }
                    }
                }
            }
        }
        if ((authenticators == null || authenticators.isEmpty())) {
            LOG.error("Authenticator with appID[{}],deviceID[{}],authType[{}],user[{}] is not registered.",
                    appID, deviceID, authType, userName);

            throw new AuthenticatorNotRegisteredException(UAFErrorCode.BIZ_AUTHENTICATOR_NOT_FOUND);
        }
        Set<Authenticator> set = new HashSet<Authenticator>();
        if(Strings.isNotBlank(keyID)){
            for(Authenticator a : authenticators){
                if(!keyID.equals(a.getKeyID().toString())){
                    set.add(a);
                }
            }
        }else {
            set.addAll(authenticators);
        }
        DeregisterationRequest request = processDereg(set, deviceID, from, appID, cancelReason);
        request.getOperationHeader().setApplicationID(this.getServerConfig().getApplicationID(appID, transType));
        if (serverConfig.getAppIdUrlBoolean(appID, transType)) {
            setFacetsAaid(request, appID);
        }
        LOG.info("Authenticator with appID[{}],deviceID[{}],authType[{}],user[{}] deregistered successfully.",
                appID, deviceID, authType, userName);
        return request;
    }
    public DeregisterationRequest deleteAll(String userName, String appID, String deviceID, List<String> authType
            , List<String> transType, String from, String cancelReason)
            throws DAOException, AuthenticatorNotRegisteredException {

        Set<Authenticator> authenticators = getAuthenticatorsOnAllDevice(userName, appID);

        authenticators = filter(authenticators, authType, transType);
        for (String authTypeString : authType) {
            if ((authTypeString.compareTo(Constants.AuthType.CERT_FINGERPRINT.getAuthType()) >= 0) || (authTypeString.compareTo(Constants.AuthType.REMOTE_FACEPRINT.getAuthType()) < 0)) {
                for (String t : transType) {
                    String applicationID = this.getServerConfig().getApplicationID(appID, t);
                    if (this.getServerConfig().getTenantShare() && com.gmrz.util.Strings.isNotBlank(applicationID)) {
                        List<String> shareAppIDList = getShareAppID(applicationID, t);
                        shareAppIDList.remove(appID);
                        if (null != shareAppIDList && !shareAppIDList.isEmpty()) {
                            for (String shareAppID : shareAppIDList) {
                                Set<Authenticator> shareAuthenticators = getAuthenticatorsOnAllDevice(userName, shareAppID);
                                if (null != shareAuthenticators && !shareAuthenticators.isEmpty()) {
                                    for (Authenticator authenticator : shareAuthenticators) {
                                        if (authenticator.getAuthType().equals(authTypeString)) {
                                            authenticators.add(authenticator);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        if ((authenticators == null || authenticators.isEmpty())) {
            LOG.error("Authenticator with appID[{}],deviceID[{}],authType[{}],user[{}] is not registered.",
                    appID, deviceID, authType, userName);

            throw new AuthenticatorNotRegisteredException(UAFErrorCode.BIZ_AUTHENTICATOR_NOT_FOUND);
        }
        // 将当前用户注册的认证数据的状态全部更新为2
        DeregisterationRequest request = processDereg(authenticators, deviceID, from, appID, cancelReason);
        request.getOperationHeader().setApplicationID(this.getServerConfig().getApplicationID(appID, transType.get(0)));
        if (serverConfig.getAppIdUrlBoolean(appID, transType.get(0))) {
            setFacetsAaid(request, appID);
        }
        LOG.info("Authenticator with appID[{}],deviceID[{}],authType[{}],user[{}] deregistered successfully.",
                appID, deviceID, authType, userName);
        return request;
    }

    private Set<Authenticator> filter(Set<Authenticator> authenticators, List<String> authType, List<String> transType) {
        Set<Authenticator> result = new HashSet<Authenticator>();
        for (Authenticator a : authenticators) {
            for (String auth : authType) {
                //for(String trans:transType){
                if (a.getAuthType().equals(auth)) {
                    result.add(a);
                }
                //}
            }
        }
        return result;
    }

    public DeregisterationRequest processDereg(Set<Authenticator> authenticators, String currentDeviceID, String from, String appID, String cancelReason) throws DAOException {
        DeregisterationRequest request = GuiceUtil.getProcessorInjector().getInstance(DeregisterationRequest.class);
        int status = Constants.AuthenticatorStatus.DELETED.getStatus();
        if (Strings.isNullOrEmpty(from))
            from = Constants.DeregFrom.CLIENT.getFrom();
        if (from.equals(Constants.DeregFrom.SERVER.getFrom())) {
            status = Constants.AuthenticatorStatus.DISABLED.getStatus();
        }
        List<DeregisterAuthenticator> deregList = new ArrayList<DeregisterAuthenticator>();
        for (Authenticator authenticator : authenticators) {
            Connection conn = null;
            try {
                conn = UAFDBConnectionMgr.getConnection(false);
                AuthenticatorDAO dao = UAFDAOFactory.createAuthenticatorDAO(conn);
                boolean delResult = false;
                if ("9999".equals(authenticator.getAppID())) {
                    delResult = dao.deleteAuth(authenticator);
                } else {
                    delResult = dao.delete(authenticator, status);
                }
                if (delResult) {
                    dao.updateDevice(authenticator.getUserName(),
                            authenticator.getAppID(),
                            authenticator.getDeviceID(),
                            authenticator.getAuthType(),
                            authenticator.getTransType());

                    if (authenticator.getAuthType().compareTo(Constants.AuthType.WEBAUTHN.getAuthType()) >= 0) {

                    } else if (authenticator.getAuthType().compareTo(Constants.AuthType.CERT_FINGERPRINT.getAuthType()) >= 0) {
                        CertServicePlugin certServicePlugin = serviceManager.getCertServicePlugin(authenticator.getAuthType());
                        CertificateBean certificateBean = dao.findCertByAuthId(authenticator.getID());
                        AuthenticatorSpecDAO specDAO = UAFDAOFactory.createAuthenticatorSpecDAO(conn);
                        AuthenticatorSpec spec = specDAO.getBySpecID(authenticator.getSpecDBID());
                        CertBean certBean = new CertBean();
                        certBean.setCertId(certificateBean.getCertId());
                        certBean.setCertText(certificateBean.getCertText());
                        certBean.setAlgorithm(spec.getAlgorithm());
                        certBean.setAuthId(authenticator.getID());

                        byte[] bytes = certServicePlugin.revokeCert(certBean,authenticator.getAppID());
                        String authID = authenticator.getID();
                        dao.deleteCertExtend(authID, appID, cancelReason);
                    } else if (authenticator.getAuthType().compareTo(Constants.AuthType.REMOTE_FACEPRINT.getAuthType()) >= 0) {
                        AuthServicePlugin plugin = serviceManager.getServicePlugin(authenticator.getAAID().format(), authenticator.getAuthType());
                        ServiceProvider provider = GuiceUtil.getProcessorInjector().getInstance(UASServiceProvider.class);
                        plugin.setServiceProvider(provider);
                        String pluginUserName = CryUtil.getSHA(authenticator.getUserName() + "_" + authenticator.getAppID() + "_" +
                                authenticator.getAuthType() + "_" + authenticator.getTransType());
                        plugin.unregister(pluginUserName);
                    }
                    if (currentDeviceID != null && currentDeviceID.equals(authenticator.getDeviceID())) {
                        DeregisterAuthenticator da = new DeregisterAuthenticator();
                        da.setAaid(authenticator.getAAID());
                        da.setKeyID(authenticator.getKeyID());
                        deregList.add(da);
                        request.setDeregisterAuthenticators(deregList);
                    }
                }
                conn.commit();
            } catch (SQLException sqle) {
                LOG.error("Failed to access Authenticator data, ", sqle);
            } catch (PluginException e) {
                LOG.error("authservice plugin unregister failed");
            } finally {
                DBUtil.close(conn);
            }

        }

        return request;
    }
}
